<?php
//$Id: Upload.php 1269 2012-12-24 10:00:30Z tomqin@gmail.com $

/**
 * Part of the framework.
 *
 * @package     Controller
 * @version     1.0
 * @copyright   Copyright (C) 2010-2012 SupeFun.com Inc.
 * @author      TomQin <tomqin@gmail.com>
 * @license     http://www.supefun.com/license/
 */

define('UPLOAD_ERR_FILE_SIZE',  10);
define('UPLOAD_ERR_FILE_TYPE',  11);
define('UPLOAD_ERR_FILE_IO',    12);
define('UPLOAD_ERR_IMAGE_TYPE', 13);
define('UPLOAD_ERR_IMAGE_SMALL',14);
define('UPLOAD_ERR_IMAGE_LARGE',15);
define('UPLOAD_ERR_UPLOAD_DIR', 16);
define('UPLOAD_ERR_UNKNOWN',    99);

class Upload {

    /**
     * 上传的文件信息 $_FILES['upfile']
     *
     * @var array
     */
    public $file = null;

    /**
     * Upload类的配置信息
     *
     * 具体配置项说明
     * - upload_dir (string): 文件存储目录（注意安全性）
     * - is_tmpfile (boolean): 是否为临时文件；默认值为 false
     * - min_length (integer): 允许最小长度(字节)；默认值为 0
     * - max_length (integer): 允许最大长度(字节)；0 为无限制
     * - allow_type (string): 可以取文件名后缀，如：'txt|pdf|docx'；若为图片，可使用
     *                      image 代替 'jpeg|jpg|jpge|gif|png'
     * - check_image (boolean): 是否检测图片，若为 true 会返回图片相关信息
     * - image_min_width (integer): 允许图片的最小宽度（像素）
     * - image_max_width (integer): 允许图片的最大宽度（像素）
     * - image_min_height (integer): 允许图片的最小高度（像素）
     * - image_max_height (integer): 允许图片的最大高度（像素）
     *
     * @var array
     */
    private $options = array();

    /**
     * Upload类的默认配置信息
     *
     * @see $options
     * @var array
     */
    private $default_options = array(
        'upload_dir'  => '',
        'is_tmpfile'  => false,
        'min_length'  => 0,
        'max_length'  => 0,
        'allow_type'  => 'image',
        'check_image' => false,
        'image_min_width'  => 0,
        'image_max_width'  => 10000,
        'image_min_height' => 0,
        'image_max_height' => 10000,
    );

    /**
     * 构造函数
     *
     * @param string    上传文件信息，仅支持单文件。如 $_FILES['upfile']
     * @param array     类的配置信息
     * @param array     错误信息列表
     */
    public function __construct($file, $options = array(), $errmsg = array()) {
        if(isset($_FILES[$file]))
            $this->file = $_FILES[$file];

        foreach($this->default_options as $key=>$var) {
            $this->options[$key] = isset($options[$key]) ? $options[$key]
                : $this->default_options[$key];
        }
    }

    /**
     * 上传文件，返回错误信息
     *
     * @return integer  文件上传错误
     */
    public function uploadFile() {
        $this->file['error'] = $this->checkUpload();
        return $this->file['error'];
    }

    /**
     * 检查上传文件是否出错
     *
     * @return integer  文件上传错误
     */
    private function checkUpload() {
        if(empty($this->options['upload_dir']) || !is_dir($this->options['upload_dir']) || !is_writeable($this->options['upload_dir'])) {
            return UPLOAD_ERR_UPLOAD_DIR;
        }

        if(!isset($this->file) || empty($this->file)) {
            return UPLOAD_ERR_NO_FILE;
        }

        $this->file['file'] = '';

        if($this->file['error']) {
            return $this->file['error'];
        }

        if(empty($this->file['tmp_name']) || empty($this->file['size'])) {
            return UPLOAD_ERR_NO_FILE;
        }

        //文件尺寸
        if($this->options['max_length'] > 0 && $this->file['size'] > $this->options['max_length']) {
            return UPLOAD_ERR_FILE_SIZE;
        }

        //文件类型
        $fileext = self::fileext($this->file['name']);
        if($this->options['allow_type']) {
            $allow_type = ($this->options['allow_type'] == 'image') ? 'jpeg|jpg|jpge|gif|png' : $this->options['allow_type'];
            if(!preg_match("/{$allow_type}/i", $fileext)) {
                return UPLOAD_ERR_FILE_TYPE;
            }
        }

        //检查图片尺寸
        if($this->options['check_image']) {
            if(($error = $this->checkImage()) != UPLOAD_ERR_OK){
                return $error;
            }
        }

        //存储文件
        $filename = ($this->options['is_tmpfile'] ? 'tmp/'.date('m/YmdHis') : date('Y/m/d/YmdHis')).'_'.mt_rand(1000, 9999);
        $thefile  = $filename.'.'.$fileext;

        $thedir   = $this->options['upload_dir'].dirname($filename);
        if(!is_dir($thedir)){
            if(is_file($thedir)) unlink($thedir);
            mkdir($thedir, 0777, true);
        }
        if(!move_uploaded_file($this->file['tmp_name'], $this->options['upload_dir'].$thefile)){
            return UPLOAD_ERR_FILE_IO;
        }else{
            $this->file['file'] = $thefile; //上传文件
        }
        return UPLOAD_ERR_OK;
    }

    /**
     * 检查上传图片是否出错
     *
     * @return integer  图片上传错误
     */
    private function checkImage() {
        $this->file['imageinfo'] = getimagesize($this->file['tmp_name']);
        if(!$this->file['imageinfo']) {
            return UPLOAD_ERR_IMAGE_TYPE;
        } else {
            if($this->options['check_image']) {
                list($width, $height, $type, $attr) = $this->file['imageinfo'];
                if($width  < $this->options['image_min_width'] ||
                   $height < $this->options['image_min_height'])
                {
                    return UPLOAD_ERR_IMAGE_SMALL;
                }

                if($width  > $this->options['image_max_width'] ||
                   $height > $this->options['image_max_height'])
                {
                    return UPLOAD_ERR_IMAGE_LARGE;
                }
            }
        }

        return UPLOAD_ERR_OK;
    }

    public function getError($no) {
        $error = array(
            UPLOAD_ERR_OK          => '文件上传成功', //0
            UPLOAD_ERR_INI_SIZE    => 'UPLOAD_ERR_INI_SIZE',    //1
            UPLOAD_ERR_FORM_SIZE   => 'UPLOAD_ERR_FORM_SIZE',
            UPLOAD_ERR_PARTIAL     => 'UPLOAD_ERR_PARTIAL',
            UPLOAD_ERR_NO_FILE     => 'UPLOAD_ERR_NO_FILE',
            UPLOAD_ERR_NO_TMP_DIR  => 'UPLOAD_ERR_NO_TMP_DIR',
            UPLOAD_ERR_CANT_WRITE  => 'UPLOAD_ERR_CANT_WRITE',
            UPLOAD_ERR_EXTENSION   => 'UPLOAD_ERR_EXTENSION',

            UPLOAD_ERR_FILE_SIZE   => 'UPLOAD_ERR_FILE_SIZE',
            UPLOAD_ERR_FILE_TYPE   => 'UPLOAD_ERR_FILE_TYPE',
            UPLOAD_ERR_FILE_IO     => 'UPLOAD_ERR_FILE_IO',

            UPLOAD_ERR_IMAGE_TYPE  => 'UPLOAD_ERR_IMAGE_TYPE',
            UPLOAD_ERR_IMAGE_SMALL => 'UPLOAD_ERR_IMAGE_SMALL',
            UPLOAD_ERR_IMAGE_LARGE => 'UPLOAD_ERR_IMAGE_LARGE',

            UPLOAD_ERR_UPLOAD_DIR  => 'UPLOAD_ERR_UPLOAD_DIR',
            UPLOAD_ERR_UNKNOWN     => 'UPLOAD_ERR_UNKNOWN',
        );

        if(isset($error[$no]))
            return $error[$no];
        else
            return $error[UPLOAD_ERR_UNKNOWN];
    }

    static public function fileext($filename) {
        return strtolower(pathinfo($filename, PATHINFO_EXTENSION));
    }
}
?>